feat: add vulnerability quick-fix + hint#39
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a vulnerability code-action provider that offers quick-fix actions to update vulnerable dependencies to safer versions. It adds a new VulnerabilityCodeActionProvider that scans vulnerability diagnostics and extracts "fixed in" version information, then emits preferred quick-fix code actions to replace affected versions in documents. The diagnostic rule is enhanced to parse and surface this fixed-in version in diagnostic messages and target URLs. Supporting changes include expanded mock exports for testing and a new module alias for test configuration. Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/providers/code-actions/vulnerability.ts (1)
47-51: Minor: RedundantparseVersioncall onfixedInVersion.The
fixedInVersionextracted from the diagnostic code is already a raw semver string (e.g.,"16.1.5"), soparseVersion(fixedInVersion)?.semverwill return the same value. While this works correctly, it's slightly redundant.♻️ Optional simplification
const currentVersion = document.getText(diagnostic.range) const currentSemver = parseVersion(currentVersion)?.semver - const fixedSemver = parseVersion(fixedInVersion)?.semver ?? fixedInVersion - if (currentSemver && currentSemver === fixedSemver) + if (currentSemver && currentSemver === fixedInVersion) return []
# Conflicts: # src/index.ts
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/providers/diagnostics/rules/vulnerability.ts (1)
75-103:⚠️ Potential issue | 🟠 MajorGuard
vulnerablePackagesto avoid runtime crashes.
If the API omitsvulnerablePackages, Line 94 will throw, breaking diagnostics for the file. Default to an empty list before filtering.🔧 Suggested fix
- const { totalCounts, vulnerablePackages } = result + const { totalCounts, vulnerablePackages = [] } = result @@ - const rootVulnerabilitiesFixedIn = vulnerablePackages + const rootVulnerabilitiesFixedIn = vulnerablePackages .filter((vulnerablePackage) => vulnerablePackage.depth === 'root')
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/providers/diagnostics/rules/vulnerability.ts (2)
15-20: Clarify function intent: this finds the maximum (not earliest) fixedIn version.The reduce logic returns the maximum version from the list (since
lt(best, current)keeps the larger value). This is correct when you need to fix all vulnerabilities simultaneously—the user must upgrade to at least the highest fixedIn version. However, the PR objective references "earliest safe version," which could cause confusion.Consider renaming to
getMinimumRequiredFixVersionor adding a brief comment explaining why the maximum is chosen.📝 Suggested clarification
+/** + * Returns the highest fixedIn version, ensuring all vulnerabilities are addressed. + */ function getBestFixedInVersion(fixedInVersions: string[]): string | undefined { if (!fixedInVersions.length) return - return fixedInVersions.reduce((best, current) => lt(best, current) ? current : best) + return fixedInVersions.reduce((best, current) => lt(best, current) ? current : best) }
62-64: Reconsider preserving the version prefix in the upgrade message.Using
parsed.prefix(e.g.,^or~) in the message produces output like "Upgrade to ^16.1.5 to fix." This could be confusing because:
- The prefix implies a range, not a specific version.
- If the user's current range (e.g.,
^16.0.0) already technically includes16.1.5, the message may seem contradictory.Consider showing the exact version without a prefix in the message, or clarifying that the version number is the minimum required.
💡 Suggested fix
const fixedInVersion = getBestFixedInVersion(rootVulnerabilitiesFixedIn) const messageSuffix = fixedInVersion - ? ` Upgrade to ${parsed.prefix}${fixedInVersion} to fix.` + ? ` Upgrade to ${fixedInVersion} to fix.` : ''
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/code-actions/vulnerability.test.ts (1)
45-50: Consider also asserting theWorkspaceEditreplacement andCodeActionKind.The test only checks
titleandisPreferredviaobjectContaining. Verifying thateditcontains the correct replacement text (e.g.^1.2.3) and thatkind === CodeActionKind.QuickFixwould catch regressions in the actual fix logic, not just the label.src/providers/diagnostics/rules/vulnerability.ts (1)
55-61: Guard against duplicatefixedInvalues before selecting the best.If multiple root-level vulnerabilities report the same
fixedInversion,rootVulnerabilitiesFixedInwill contain duplicates. This is harmless for correctness (thereducestill picks the minimum), but deduplicating avoids redundant comparisons and makes intent clearer.Proposed change
const rootVulnerabilitiesFixedIn = vulnerablePackages .flatMap(({ depth, vulnerabilities }) => { if (depth !== 'root') return [] return vulnerabilities.flatMap(({ fixedIn }) => fixedIn ? [fixedIn] : []) }) - const fixedInVersion = getBestFixedInVersion(rootVulnerabilitiesFixedIn) + const fixedInVersion = getBestFixedInVersion([...new Set(rootVulnerabilitiesFixedIn)])
Closes #24